//	mpsf.noteseq.core.c

#include "mpsf.noteseq.core.h"

t_class *ns_class;

__declspec(dllexport) void main(void)
{
	t_object *attr;
	ns_class = class_new("mpsf.noteseq.core", (method)ns_new, (method)ns_free,
		(long)sizeof(t_noteseq), (method)NULL, A_GIMME, 0L);
	class_addmethod(ns_class, (method)ns_setnote, "setnote", A_LONG, A_LONG, A_LONG, 0L);
	class_addmethod(ns_class, (method)ns_clearseq, "clearseq", 0L);
	class_addmethod(ns_class, (method)ns_step, "step", A_LONG, 0L);
	class_addmethod(ns_class, (method)ns_flushnotes, "flushnotes", 0L);
	class_addmethod(ns_class, (method)object_obex_quickref, "quickref", A_CANT, 0L);
	class_addmethod(ns_class, (method)object_obex_dumpout, "dumpout", A_CANT, 0L);
	// attr..?
	class_register(CLASS_BOX, ns_class);
}

void* ns_new(long numsteps)
{
	t_noteseq *x;
	long i;
	if(numsteps < 0)
	{
		error("numsteps must be >= 0");
		return NULL;
	}
	x = (t_noteseq*)object_alloc(ns_class);
	x->_numsteps = numsteps;
	x->_curstep = -1;
	x->_prevnotes = (t_velocity*)sysmem_newptrclear((long)sizeof(t_velocity) * (PITCH_MAX+1));
	x->_noteseq = (t_velocity**)sysmem_newptr((long)sizeof(t_velocity*) * x->_numsteps);
	for(i = 0; i < x->_numsteps; i++)
		x->_noteseq[i] = (t_velocity*)sysmem_newptrclear((long)sizeof(t_velocity) * (PITCH_MAX+1));
	//x->_notemesgout = outlet_new(x, NULL);
	x->_notemesgout = listout(x);
	return x;
}

void ns_free(t_noteseq *x)
{
	long i;
	sysmem_freeptr(x->_prevnotes);
	for(i = 0; i < x->_numsteps; i++)
		sysmem_freeptr(x->_noteseq[i]);
	sysmem_freeptr(x->_noteseq);
	freeobject((t_object*)x);
}

void ns_setnote(t_noteseq *x, long step, long pitch, long velocity)
{
	if(step < 0 || step >= x->_numsteps)
	{
		error("step value [%ld] is out of range", step);
		return;
	} else if(pitch < 0 || pitch > PITCH_MAX) {
		error("pitch value [%ld] is out of range", pitch);
		return;
	} else if(velocity < 0 || velocity > VEL_MAX) {
		error("velocity value [%ld] is out of range", velocity);
		return;
	}
	x->_noteseq[step][pitch] = (t_velocity)velocity;
}

void ns_clearseq(t_noteseq *x)
{
	long s, p;
	for(s = 0; s < x->_numsteps; s++)
		for(p = 0; p <= PITCH_MAX; p++)
			x->_noteseq[s][p] = (t_velocity)0;
}

void ns_step(t_noteseq *x, long step)
{
	long p;
	t_velocity v;
	x->_curstep = step;
	for(p = 0; p <= PITCH_MAX; p++)
	{
		v = x->_noteseq[step][p];
		if(x->_prevnotes[p] == 0)
		{
			if(v != 0)
			{
				_ns_outputnoteon(x, p, v);
				x->_prevnotes[p] = v;
			}
		} else {
			if(v == 0)
			{
				_ns_outputnoteoff(x, p);
				x->_prevnotes[p] = 0;
			} else if(v != x->_prevnotes[p]) {
				_ns_outputnoteoff(x, p);
				_ns_outputnoteon(x, p, v);
				x->_prevnotes[p] = v;
			}
		}
	}
}

void ns_flushnotes(t_noteseq *x)
{
	long p;
	for(p = 0; p <= PITCH_MAX; p++)
	{
		if(x->_prevnotes[p])
		{
			_ns_outputnoteoff(x, p);
			x->_prevnotes[p] = (t_velocity)0;
		}
	}
}

void _ns_outputnoteon(t_noteseq *x, t_pitch pitch, t_velocity velocity)
{
	t_atom argv[2];
	SETLONG(&argv[0], (long)pitch);
	SETLONG(&argv[1], (long)velocity);
	outlet_list(x->_notemesgout, 0L, (short)2, argv);
}

void _ns_outputnoteoff(t_noteseq *x, t_pitch pitch)
{
	t_atom argv[2];
	SETLONG(&argv[0], (long)pitch);
	SETLONG(&argv[1], 0L);
	outlet_list(x->_notemesgout, 0L, (short)2, argv);
}


